package com.jml.netty.心跳检测;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.SocketAddress;


/**
 * 服务端代码 长连接
 */
public class HeartBeatServer {


    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap()
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new StringEncoder());
                        pipeline.addLast(new StringDecoder());
                        //IdleStateHandler的readerIdleTime参数指定超过3秒还没收到客户端的连接，
                        //会触发IdleStateEvent事件并且交给下一个handler处理，下一个handler必须
                        //实现userEventTriggered方法处理对应事件
                        /**
                         * readerIdleTimeSeconds  读操作空闲3s
                         * writerIdleTimeSeconds  写操作空闲0秒
                         * allIdleTimeSeconds  读写全部 0
                         */
                        pipeline.addLast(new IdleStateHandler(3, 0, 0));
                        pipeline.addLast(new HeartBeatServerHandler());
                    }
                });
        ChannelFuture future = bootstrap.bind(9000).sync();
        System.out.println("netty server start successfully");
        future.channel().closeFuture().sync();
    }
}

class HeartBeatServerHandler extends SimpleChannelInboundHandler<String> {

    private int readIdleTimes = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        System.out.println("====== > [server] message received: " + msg);
        ctx.channel().writeAndFlush("ok");
    }

    /**
     * 重写这个方法 ：
     * 用户事件触发
     *
     * @param ctx
     * @param evt
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        IdleStateEvent event = (IdleStateEvent) evt;
        String eventType;
        switch (event.state()) {
            case READER_IDLE:
                //超过3秒没有发送数据就会进入这里进行次数的累加
                eventType = "读空闲";
                //读空闲的计数加1
                System.out.println(readIdleTimes);
                readIdleTimes++;
                System.out.println(readIdleTimes);
                break;
            case WRITER_IDLE:
                eventType = "写空闲";
                // 不处理
                break;
            case ALL_IDLE:
                eventType = "读写空闲";
                // 不处理
                break;
            default:
                throw new IllegalStateException("非法状态！");
        }
        System.out.println(ctx.channel().remoteAddress() + "超时事件：" + eventType);
        //3秒记录一次，所以一共是9秒就会断开连接
        if (readIdleTimes > 3) {
            System.out.println("[server]读空闲超过3次，关闭连接，释放更多资源");
            ctx.channel().writeAndFlush("idle close");
            ctx.channel().close();
        }
    }

    /**
     * 活跃连接
     *
     * @param ctx
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("=== " + ctx.channel().remoteAddress() + " is active ===");
    }

    /**
     * 客户端与服务器端端口连接
     * 客户端主动断开连接会调这个方法
     *
     * @param ctx
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        SocketAddress address = channel.remoteAddress();
        System.out.println(address + " 下线了");
    }
}